package com.session;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Maps;
import io.netty.channel.Channel;
import org.apache.commons.collections4.CollectionUtils;


import javax.annotation.concurrent.ThreadSafe;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@ThreadSafe
public class ChatServerContextImpl implements ChatServerContext {

    /**
     * biMap (bidirectional map) 双向map
     * Google的Guava提供了BiMap这样一个双向Map，调用inverse()方法会返回一个反向的关联的BiMap，然后便可以通过get()方法获取key值了。
     * 需要注意的是，BiMap作为一个双向的Map，它不能存储多对一的关系；而HashMap是可以的。其实很好理解，因为是双向的，
     * 所以即要满足Key值的唯一性，也要满足Value值的唯一性。如果往里存放同样的Value，
     * 会抛异常：java.lang.IllegalArgumentException: value already present。
     *
     */
    private final BiMap<String, Channel> userNameMappingSocketChannelMap = Maps.synchronizedBiMap(HashBiMap.<String, Channel>create());//new ConcurrentHashMap<>();

    /**
     * 类似地，Apache Commons Collections也提供了双向Map的类BidiMap，
     * 它也是维持一对一的关系，不能多对一。它提供了getKey(value)方法返回Key值。代码如下：
     */
//    DualHashBidiMap<String,SocketChannel> userNameMappingSocketChannelBidiMap=new DualHashBidiMap<String,SocketChannel>();

    @Override
    public Channel getChannelByUserName(String userName) {
        return this.userNameMappingSocketChannelMap.get(userName);
    }

    @Override
    public boolean deleteChannelByUserName(String userName) {
        return this.userNameMappingSocketChannelMap.remove(userName) != null;
    }

    @Override
    public void addUserNameAndChannel(String userName, Channel socketChannel) {
        this.userNameMappingSocketChannelMap.put(userName, socketChannel);
    }

    @Override
    public String getUserNameByChannel(Channel socketChannel) {
        /**
         * 可以将 map 的 key 作为 value ，value 作为 key
         */
        return this.userNameMappingSocketChannelMap.inverse().get(socketChannel);
    }

    @Override
    public Map<String, Channel> getUserNameMappingChannelMapByBatchUserNames(Collection<String> initGroupMembers) {

        return initGroupMembers.stream().filter(this.userNameMappingSocketChannelMap::containsKey).collect(Collectors.toMap(memberName->memberName,this.userNameMappingSocketChannelMap::get));
    }
}
